﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Backend
{
    /// <summary>
    /// Data Stream Interface.  Multiple stream protocols can implement from this in the future.
    /// </summary>
    public interface IDataStream
    {
        void Parse(byte[] Buffer, int DataLength);

        /// <summary>
        /// Event fired when a complete packet is received.
        /// </summary>
        event Action<byte[]> OnPacket;
    }

    /// <summary>
    /// Returns a stream of data packets from an agreed upon protocol.  This Protocol has a 10 byte header.
    /// </summary>
    public class DataStream : IDataStream
    {
        private byte[] InternalBuffer;

        public event Action<byte[]> OnPacket;

        public DataStream()
        {
            InternalBuffer = new byte[0];
        }

        private void PacketReceived(byte[] Packet)
        {
            if (OnPacket != null)
                OnPacket(Packet);
        }

        /// <summary>
        /// Accepts bytes and parses them into the protocols standard.  Stores an internal buffer that manages incomplete packets.
        /// </summary>
        /// <param name="Buffer"></param>
        /// <param name="DataLength">Length of bytes received from client.</param>
        public void Parse(byte[] Buffer, int DataLength)
        {

                //Copy over the internal buffer to grab incomplete packets from last receive.
            byte[] Payload = new byte[DataLength + InternalBuffer.Length];
            Array.Copy(InternalBuffer, Payload, InternalBuffer.Length);
            Array.Copy(Buffer, 0, Payload, InternalBuffer.Length, DataLength);

            using (MemoryStream ms = new MemoryStream(Payload))
            {
                BinaryReader br = new BinaryReader(ms);

                short PacketLength = br.ReadInt16();

                ms.Position -= sizeof(short);

                int BufferRemaining = Payload.Length;

                while (PacketLength <= BufferRemaining)
                {
                    byte[] NewPacket = br.ReadBytes(PacketLength);
                    
                    //Must have been a bad packet, so delete old buffer of potentially bad packets and start listening again.
                    if (NewPacket.Length <= 0)
                    {
                        InternalBuffer = new byte[0];
                        return;
                    }

                    PacketReceived(NewPacket);

                    BufferRemaining -= PacketLength;

                    if (BufferRemaining < 2)
                        break;
                    PacketLength = br.ReadInt16();
                    ms.Position -= sizeof(short);
                }

                if (BufferRemaining != 0)
                {
                    InternalBuffer = new byte[BufferRemaining];
                    Array.Copy(Payload, Payload.Length - BufferRemaining, InternalBuffer, 0, BufferRemaining);
                }
                else
                    InternalBuffer = new byte[0];
            }
        }

    }
}
